package cn.bugDance.infrastructure.persistent.repository;

import cn.bugDance.domain.activity.event.ActivitySkuStockOverEvent;
import cn.bugDance.domain.activity.model.aggregate.ActivityPartakeOrderAggregate;
import cn.bugDance.domain.activity.model.aggregate.ActivitySkuOrderAggregate;
import cn.bugDance.domain.activity.model.entity.*;
import cn.bugDance.domain.activity.model.valobj.*;
import cn.bugDance.domain.activity.repository.IActivityRepository;
import cn.bugDance.infrastructure.annotation.CacheQuery;
import cn.bugDance.infrastructure.event.EventPublisher;
import cn.bugDance.infrastructure.persistent.dao.*;
import cn.bugDance.infrastructure.persistent.po.*;
import cn.bugDance.infrastructure.persistent.redis.IRedisService;
import cn.bugDance.types.common.Constants;
import cn.bugDance.types.enums.ResponseCode;
import cn.bugDance.types.exception.AppException;
import cn.bugstack.middleware.db.router.strategy.IDBRouterStrategy;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBlockingQueue;
import org.redisson.api.RDelayedQueue;
import org.redisson.api.RLock;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.support.TransactionTemplate;


import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Repository
@Slf4j
public class ActivityRepository implements IActivityRepository {

    @Resource
    private TransactionTemplate transactionTemplate;
    @Resource
    private IDBRouterStrategy dbRouter;
    @Resource
    private IActivityAccountDao activityAccountDao;
    @Resource
    private IActivitySkuOrderDao activitySkuOrderDao;
    @Resource
    private IActivityDao activityDao;
    @Resource
    private IRedisService redisService;
    @Resource
    private IActivitySkuDao activitySkuDao;
    @Resource
    private IActivityCountDao activityCountDao;
    @Resource
    private EventPublisher eventPublisher;
    @Resource
    private IActivityPartakeOrderDao activityPartakeOrderDao;
    @Resource
    private IActivityMonthAccountDao activityMonthAccountDao;
    @Resource
    private IActivityDayAccountDao activityDayAccountDao;






    @Override
    public ActivityAccountEntity queryActivityAccountByUserIdAndActivityId(String userId, Long activityId) {

        ActivityAccountEntity activityAccountEntity = null;
        try{
            dbRouter.doRouter(userId);
            ActivityAccount activityAccount = activityAccountDao.getActivityAccountByUserIdAndActivityId(userId, activityId);
            if(null != activityAccount){
                activityAccountEntity = new ActivityAccountEntity();
                activityAccountEntity.setActivityId(activityId);
                activityAccountEntity.setUserId(userId);
                activityAccountEntity.setTotalCount(activityAccount.getTotalCount());
                activityAccountEntity.setTotalCountSurplus(activityAccount.getTotalCountSurplus());
                activityAccountEntity.setMonthCount(activityAccount.getMonthCount());
                activityAccountEntity.setDayCount(activityAccount.getDayCount());
            }
        }finally {
            dbRouter.clear();
        }
        return activityAccountEntity;
    }

    @Override
    public void insertActivityAccount(ActivityAccountEntity activityAccountEntity) {
        try{
            dbRouter.doRouter(activityAccountEntity.getUserId());

            ActivityAccount activityAccount = new ActivityAccount();
            activityAccount.setUserId(activityAccountEntity.getUserId());
            activityAccount.setActivityId(activityAccountEntity.getActivityId());
            activityAccount.setTotalCount(activityAccountEntity.getTotalCount());
            activityAccount.setMonthCount(activityAccountEntity.getMonthCount());
            activityAccount.setDayCount(activityAccountEntity.getDayCount());

            activityAccountDao.insert(activityAccount);
        } catch (DuplicateKeyException e){
            log.error("【活动仓储层-ActivityRepository】: 创建用户活动账户主键冲突, userId: {}, activityId: {}, message: {}", activityAccountEntity.getUserId(), activityAccountEntity.getActivityId(), e.getMessage());
            throw new AppException(ResponseCode.DUPLICATE_KEY_EXCEPTION.getCode(), ResponseCode.DUPLICATE_KEY_EXCEPTION.getInfo());
        }finally{
            dbRouter.clear();
        }
    }

    @Override
    public void createActivitySkuOrder(Long activityId, String userId, String bizId) {

    }

    @Override
    @CacheQuery(cacheKeyPrefix = Constants.Redis.ACTIVITY)
    public ActivityEntity queryActivityByActivityId(Long activityId) {

        Activity activity = activityDao.getActivityByActivityId(activityId);
        ActivityEntity activityEntity = new ActivityEntity();
        activityEntity.setStrategyId(activity.getStrategyId());
        activityEntity.setActivityId(activityId);
        activityEntity.setActivityDesc(activity.getActivityDesc());
        activityEntity.setActivityName(activity.getActivityName());
        activityEntity.setState(ActivityStateVO.valueOf(activity.getState()));
        activityEntity.setBeginTime(activity.getBeginTime());
        activityEntity.setEndTime(activity.getEndTime());
        return activityEntity;
    }

    @Override
    @CacheQuery(cacheKeyPrefix = Constants.Redis.SKU)
    public ActivitySkuEntity queryActivitySkuBySku(Long sku) {

        ActivitySkuEntity activitySkuEntity = new ActivitySkuEntity();
        ActivitySku activitySku = activitySkuDao.getActivitySkuBySku(sku);
        activitySkuEntity.setSku(sku);
        activitySkuEntity.setActivityId(activitySku.getActivityId());
        activitySkuEntity.setActivityCountId(activitySku.getActivityCountId());
        activitySkuEntity.setStockCount(activitySku.getStockCount());
        activitySkuEntity.setPayAmount(activitySku.getPayAmount());
        return activitySkuEntity;
    }

    @Override
    @CacheQuery(cacheKeyPrefix = Constants.Redis.ACTIVITY_COUNT)
    public ActivityCountEntity queryActivityCountByCountId(Long activityCountId) {

        ActivityCount activityCount = activityCountDao.getActivityCountByCountId(activityCountId);
        ActivityCountEntity activityCountEntity = new ActivityCountEntity();
        activityCountEntity.setActivityCountId(activityCountId);
        activityCountEntity.setTotalCount(activityCount.getTotalCount());
        activityCountEntity.setMonthCount(activityCount.getMonthCount());
        activityCountEntity.setDayCount(activityCount.getDayCount());
        return activityCountEntity;
    }

    @Override
    public boolean subtractionActivitySkuStock(Long sku, Date endTime) {
        String cacheKey = Constants.Redis.ACTIVITY_SKU_COUNT + sku;

        long skuCount = redisService.decr(cacheKey);
        if(skuCount < 0){
            redisService.setValue(cacheKey, 0);
            return false;
        }
        if(skuCount == 0){
            eventPublisher.publish(ActivitySkuStockOverEvent.topic, ActivitySkuStockOverEvent.create(sku));
            return true;
        }
        long expireMills = endTime.getTime() - System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1);
        String cacheLockKey = Constants.Redis.ACTIVITY_SKU_COUNT_LOCK + sku + ":" + (skuCount + 1);
        return redisService.setNx(cacheLockKey, expireMills, TimeUnit.MILLISECONDS);
    }

    @Override
    public void sendJobQueue(ActivitySkuStockSubtractionJobData activitySkuStockSubtractionJobData) {
        String cacheKey = Constants.Redis.ACTIVITY_DELAY_JOB_QUEUE + activitySkuStockSubtractionJobData.getSku();
        RBlockingQueue<ActivitySkuStockSubtractionJobData> blockingQueue = redisService.getBlockingQueue(cacheKey);
        RDelayedQueue<ActivitySkuStockSubtractionJobData> delayedQueue = redisService.getDelayedQueue(blockingQueue);
        delayedQueue.offer(activitySkuStockSubtractionJobData, 3, TimeUnit.SECONDS);
    }

    @Override
    public ActivitySkuStockSubtractionJobData takeJobQueue(Long sku) {
        String cacheKey = Constants.Redis.ACTIVITY_DELAY_JOB_QUEUE + sku;
        RBlockingQueue<ActivitySkuStockSubtractionJobData> blockingQueue = redisService.getBlockingQueue(cacheKey);

        return blockingQueue.poll();
    }

    @Override
    public void activitySkuStockSubtractQuota(Long sku) {
        activitySkuDao.activitySkuStockSubtractQuota(sku);
    }

    @Override
    public void storeActivitySkuStock(Long sku, Integer stockCount, Date endTime) {
        String cacheKey = Constants.Redis.ACTIVITY_SKU_COUNT + sku;
        long expireMills = endTime.getTime() - System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1);
        redisService.setValue(cacheKey, stockCount, expireMills);
    }

    @Override
    public ActivitySkuEntity assembleActivitySku(Long sku) {
        String cacheKey = Constants.Redis.SKU + sku;
        ActivitySkuEntity activitySkuEntity = new ActivitySkuEntity();
        ActivitySku activitySku = activitySkuDao.getActivitySkuBySku(sku);
        activitySkuEntity.setSku(sku);
        activitySkuEntity.setActivityId(activitySku.getActivityId());
        activitySkuEntity.setActivityCountId(activitySku.getActivityCountId());
        activitySkuEntity.setStockCount(activitySku.getStockCount());
        activitySkuEntity.setPayAmount(activitySku.getPayAmount());
        activitySkuEntity.setIsRecharge(activitySku.getIsRecharge());
        redisService.setValue(cacheKey, activitySkuEntity);
        return activitySkuEntity;
    }

    @Override
    public ActivityEntity assembleActivity(Long activityId) {
        String cacheKey = Constants.Redis.ACTIVITY + activityId;

        Activity activity = activityDao.getActivityByActivityId(activityId);
        ActivityEntity activityEntity = new ActivityEntity();
        activityEntity.setStrategyId(activity.getStrategyId());
        activityEntity.setActivityId(activityId);
        activityEntity.setActivityDesc(activity.getActivityDesc());
        activityEntity.setActivityName(activity.getActivityName());
        activityEntity.setState(ActivityStateVO.valueOf(activity.getState()));
        activityEntity.setBeginTime(activity.getBeginTime());
        activityEntity.setEndTime(activity.getEndTime());
        redisService.setValue(cacheKey, activityEntity);
        return activityEntity;
    }

    @Override
    public ActivityCountEntity assembleActivityCount(Long activityCountId) {
        String cacheKey = Constants.Redis.ACTIVITY_COUNT + activityCountId;
        ActivityCount activityCount = activityCountDao.getActivityCountByCountId(activityCountId);
        ActivityCountEntity activityCountEntity = new ActivityCountEntity();
        activityCountEntity.setActivityCountId(activityCountId);
        activityCountEntity.setTotalCount(activityCount.getTotalCount());
        activityCountEntity.setMonthCount(activityCount.getMonthCount());
        activityCountEntity.setDayCount(activityCount.getDayCount());
        redisService.setValue(cacheKey, activityCountEntity);
        return activityCountEntity;
    }

    @Override
    public void saveOrder(ActivitySkuOrderAggregate activitySkuOrderAggregate) {
        try{
            String userId = activitySkuOrderAggregate.getUserId();
            Long activityId = activitySkuOrderAggregate.getActivityId();
            Long sku = activitySkuOrderAggregate.getSku();
            String month = activitySkuOrderAggregate.getMonth();
            String day = activitySkuOrderAggregate.getDay();

            ActivitySkuOrderEntity activitySkuOrderEntity = activitySkuOrderAggregate.getActivitySkuOrderEntity();
            ActivityCountEntity activityCountEntity = activitySkuOrderAggregate.getActivityCountEntity();

            ActivitySkuOrder activitySkuOrder = new ActivitySkuOrder();
            activitySkuOrder.setUserId(userId);
            activitySkuOrder.setActivityId(activityId);
            activitySkuOrder.setSku(sku);
            activitySkuOrder.setStrategyId(activitySkuOrderEntity.getStrategyId());
            activitySkuOrder.setOrderId(activitySkuOrderEntity.getOrderId());
            activitySkuOrder.setActivityName(activitySkuOrderEntity.getActivityName());
            activitySkuOrder.setOrderTime(activitySkuOrderEntity.getOrderTime());
            activitySkuOrder.setTotalCount(activityCountEntity.getTotalCount());
            activitySkuOrder.setMonthCount(activitySkuOrderEntity.getMonthCount());
            activitySkuOrder.setDayCount(activitySkuOrderEntity.getDayCount());
            activitySkuOrder.setState(activitySkuOrderEntity.getState().getCode());
            activitySkuOrder.setOutBusinessId(activitySkuOrderEntity.getOutBusinessId());
            activitySkuOrder.setTradeType(activitySkuOrderEntity.getTradeType().getCode());

            ActivityAccount activityAccount = new ActivityAccount();
            activityAccount.setUserId(userId);
            activityAccount.setActivityId(activityId);
            activityAccount.setTotalCount(activityCountEntity.getTotalCount());
            activityAccount.setMonthCount(activityCountEntity.getMonthCount());
            activityAccount.setDayCount(activityCountEntity.getDayCount());

            ActivityMonthAccount activityMonthAccount = new ActivityMonthAccount();
            activityMonthAccount.setUserId(userId);
            activityMonthAccount.setActivityId(activityId);
            activityMonthAccount.setMonth(month);
            activityMonthAccount.setMonthCount(activityCountEntity.getTotalCount() + activityCountEntity.getMonthCount());
            activityMonthAccount.setMonthCountSurplus(activityCountEntity.getTotalCount());


            ActivityDayAccount activityDayAccount = new ActivityDayAccount();
            activityDayAccount.setActivityId(activityId);
            activityDayAccount.setUserId(userId);
            activityDayAccount.setDay(day);
            activityDayAccount.setDayCount(activityCountEntity.getTotalCount() + activityCountEntity.getDayCount());
            activityDayAccount.setDayCountSurplus(activityCountEntity.getTotalCount());


            dbRouter.doRouter(userId);
            transactionTemplate.execute(status -> {
                try{
                    activitySkuOrderDao.insert(activitySkuOrder);
                    int countTotal = activityAccountDao.updateActivityAccount(activityAccount);
                    if(countTotal <= 0){
                        activityAccountDao.insert(activityAccount);
                    }

                    activityMonthAccountDao.updateActivityMonthAccount(activityMonthAccount);
                    activityDayAccountDao.updateActivityDayAccount(activityDayAccount);

                    return 0;
                }catch (DuplicateKeyException e){
                    status.setRollbackOnly();
                    throw new AppException(ResponseCode.DUPLICATE_KEY_EXCEPTION.getCode(), ResponseCode.DUPLICATE_KEY_EXCEPTION.getInfo());
                }
            });
        }finally {
            dbRouter.clear();
        }
    }

    @Override
    public ActivityPartakeOrderEntity queryActivityPartakeOrderEntity(String userId, Long activityId) {
        ActivityPartakeOrderEntity activityPartakeOrderEntity = null;
        try{
            dbRouter.doRouter(userId);
            ActivityPartakeOrder activityPartakeOrder = activityPartakeOrderDao.getActivityPartakeOrder(userId, activityId);

            if(activityPartakeOrder != null){
                activityPartakeOrderEntity = new ActivityPartakeOrderEntity();
                activityPartakeOrderEntity.setActivityId(activityId);
                activityPartakeOrderEntity.setUserId(userId);
                activityPartakeOrderEntity.setOrderId(activityPartakeOrder.getOrderId());
                activityPartakeOrderEntity.setOrderTime(activityPartakeOrder.getOrderTime());
                activityPartakeOrderEntity.setState(ActivityPartakeOrderStateVO.valueOf(activityPartakeOrder.getState()));
            }
        }finally {
            dbRouter.clear();
        }
        return activityPartakeOrderEntity;
    }

    @Override
    public ActivityMonthAccountEntity queryActivityMonthAccountEntity(String userId, Long activityId, String month) {
        ActivityMonthAccountEntity activityMonthAccountEntity = null;
        try{
            dbRouter.doRouter(userId);

            ActivityMonthAccount activityMonthAccount = activityMonthAccountDao.getActivityMonthAccount(userId, activityId, month);
            if(activityMonthAccount != null){
                activityMonthAccountEntity = new ActivityMonthAccountEntity();
                activityMonthAccountEntity.setActivityId(activityId);
                activityMonthAccountEntity.setUserId(userId);
                activityMonthAccountEntity.setMonth(month);
                activityMonthAccountEntity.setMonthCount(activityMonthAccount.getMonthCount());
                activityMonthAccountEntity.setMonthCountSurplus(activityMonthAccount.getMonthCountSurplus());
            }
        }finally {
            dbRouter.clear();
        }
        return activityMonthAccountEntity;
    }

    @Override
    public ActivityDayAccountEntity queryActivityDayAccountEntity(String userId, Long activityId, String day) {
        ActivityDayAccountEntity activityDayAccountEntity = null;
        try{
            dbRouter.doRouter(userId);

            ActivityDayAccount activityDayAccount = activityDayAccountDao.getActivityDayAccount(userId, activityId, day);
            if(activityDayAccount != null){
                activityDayAccountEntity = new ActivityDayAccountEntity();
                activityDayAccountEntity.setActivityId(activityId);
                activityDayAccountEntity.setUserId(userId);
                activityDayAccountEntity.setDay(day);
                activityDayAccountEntity.setDayCount(activityDayAccount.getDayCount());
                activityDayAccountEntity.setDayCountSurplus(activityDayAccount.getDayCountSurplus());
            }

        }finally {
            dbRouter.clear();
        }
        return activityDayAccountEntity;
    }

    @Override
    public void saveActivityPartakeOrder(ActivityPartakeOrderAggregate activityPartakeOrderAggregate) {
        try{
            String userId = activityPartakeOrderAggregate.getUserId();
            Long activityId = activityPartakeOrderAggregate.getActivityId();

            ActivityPartakeOrderEntity activityPartakeOrderEntity = activityPartakeOrderAggregate.getActivityPartakeOrderEntity();
            ActivityMonthAccountEntity activityMonthAccountEntity = activityPartakeOrderAggregate.getActivityMonthAccountEntity();
            ActivityDayAccountEntity activityDayAccountEntity = activityPartakeOrderAggregate.getActivityDayAccountEntity();
            Boolean isExistMonthAccount = activityPartakeOrderAggregate.getIsExistActivityMonthAccount();
            Boolean isExistDayAccount = activityPartakeOrderAggregate.getIsExistActivityDayAccount();
            String month = activityPartakeOrderAggregate.getMonth();
            String day = activityPartakeOrderAggregate.getDay();

            dbRouter.doRouter(userId);
            transactionTemplate.execute(status -> {
                try{
                    /**
                     * 不存在月,日账户, 则创建月,日账户
                     * 保存活动参与订单
                     */
                    if(isExistMonthAccount){
                        int monthCount = activityMonthAccountDao.update(userId, activityId, month);

                        if(monthCount <= 0){
                            log.warn("【活动仓储层-ActivityRepository】: 月账户不足, userId: {}, activityId: {}", userId, activityId);
                            status.setRollbackOnly();
                            throw new AppException(ResponseCode.ACTIVITY_ACCOUNT_MONTH_COUNT_ERROR.getCode(), ResponseCode.ACTIVITY_ACCOUNT_MONTH_COUNT_ERROR.getInfo());
                        }
                    }
                    else{
                        ActivityMonthAccount activityMonthAccount = new ActivityMonthAccount();
                        activityMonthAccount.setUserId(userId);
                        activityMonthAccount.setActivityId(activityId);
                        activityMonthAccount.setMonth(month);
                        activityMonthAccount.setMonthCount(activityMonthAccountEntity.getMonthCount());
                        activityMonthAccount.setMonthCountSurplus(activityMonthAccountEntity.getMonthCountSurplus() - 1);
                        activityMonthAccountDao.insert(activityMonthAccount);
                    }
                    if(isExistDayAccount){
                        int dayCount = activityDayAccountDao.update(userId, activityId, day);

                        if(dayCount <= 0){
                            log.warn("【活动仓储层-ActivityRepository】: 日账户不足, userId: {}, activityId: {}", userId, activityId);
                            status.setRollbackOnly();
                            throw new AppException(ResponseCode.ACTIVITY_ACCOUNT_DAY_COUNT_ERROR.getCode(), ResponseCode.ACTIVITY_ACCOUNT_DAY_COUNT_ERROR.getInfo());
                        }
                    }
                    else{
                        ActivityDayAccount activityDayAccount = new ActivityDayAccount();
                        activityDayAccount.setActivityId(activityId);
                        activityDayAccount.setUserId(userId);
                        activityDayAccount.setDay(day);
                        activityDayAccount.setDayCount(activityDayAccountEntity.getDayCount());
                        activityDayAccount.setDayCountSurplus(activityDayAccountEntity.getDayCountSurplus() - 1);

                        activityDayAccountDao.insert(activityDayAccount);
                    }

                    int totalCount = activityAccountDao.update(userId, activityId);
                    if(totalCount <= 0){
                        log.warn("【活动仓储层-ActivityRepository】: 总账户不足, userId: {}, activityId: {}", userId, activityId);
                        status.setRollbackOnly();
                        throw new AppException(ResponseCode.ACTIVITY_ACCOUNT_TOTAL_COUNT_ERROR.getCode(), ResponseCode.ACTIVITY_ACCOUNT_TOTAL_COUNT_ERROR.getInfo());
                    }

                    ActivityPartakeOrder activityPartakeOrder = new ActivityPartakeOrder();
                    activityPartakeOrder.setUserId(userId);
                    activityPartakeOrder.setActivityId(activityId);
                    activityPartakeOrder.setOrderId(activityPartakeOrderEntity.getOrderId());
                    activityPartakeOrder.setOrderTime(activityPartakeOrderEntity.getOrderTime());
                    activityPartakeOrder.setState(activityPartakeOrderEntity.getState().getCode());

                    activityPartakeOrderDao.insert(activityPartakeOrder);
                    return 0;
                }catch (DuplicateKeyException e){
                    log.warn("【活动仓储层-ActivityRepository】: 插入数据库主键冲突, userId: {}, activityId: {}", userId, activityId);
                    status.setRollbackOnly();
                    throw new AppException(ResponseCode.DUPLICATE_KEY_EXCEPTION.getCode(), ResponseCode.DUPLICATE_KEY_EXCEPTION.getInfo());
                }
            });
        }finally {
            dbRouter.clear();
        }
    }

    @Override
    public Integer queryUserDayCount(String userId, Long activityId, String day) {
        try{
            dbRouter.doRouter(userId);
            ActivityDayAccount activityDayAccount = activityDayAccountDao.getActivityDayAccount(userId, activityId, day);
            return activityDayAccount == null ? 0 : activityDayAccount.getDayCount() - activityDayAccount.getDayCountSurplus();
        }
         finally{
            dbRouter.clear();
        }
    }

    @Override
    public List<Long> queryRelatedSkus(Long activityId) {
        return activitySkuDao.getRelatedSkus(activityId);
    }

    @Override
    public Integer queryUserTotalCountSurplus(String userId, Long activityId) {
        try{
            dbRouter.doRouter(userId);
            ActivityAccount activityAccount = activityAccountDao.getActivityAccountByUserIdAndActivityId(userId, activityId);
            if(activityAccount == null) return 0;
            return activityAccount.getTotalCountSurplus();
        }finally {
            dbRouter.clear();
        }
    }

    @Override
    public Integer queryUserMonthCountSurplus(String userId, Long activityId, String month) {
        try{
            dbRouter.doRouter(userId);
            ActivityMonthAccount activityMonthAccount = activityMonthAccountDao.getActivityMonthAccount(userId, activityId, month);
            if(activityMonthAccount == null){
                ActivityAccount activityAccount = activityAccountDao.getActivityAccountByUserIdAndActivityId(userId, activityId);
                if(activityAccount == null) return 0;
                return activityAccount.getMonthCount();
            }
            return activityMonthAccount.getMonthCountSurplus();
        }finally {
            dbRouter.clear();
        }
    }

    @Override
    public Integer queryUserDayCountSurplus(String userId, Long activityId, String day) {
        try{
            dbRouter.doRouter(userId);
            ActivityDayAccount activityDayAccount = activityDayAccountDao.getActivityDayAccount(userId, activityId, day);
            if(activityDayAccount == null){
                ActivityAccount activityAccount = activityAccountDao.getActivityAccountByUserIdAndActivityId(userId, activityId);
                if(activityAccount == null) return 0;
                return activityAccount.getDayCount();
            }
            return activityDayAccount.getDayCountSurplus();
        }finally {
            dbRouter.clear();
        }
    }

    @Override
    public Integer queryRaffleActivityAccountPartakeCount(String userId, Long activityId) {
       try{
           dbRouter.doRouter(userId);
           ActivityAccount activityAccount = activityAccountDao.getActivityAccountByUserIdAndActivityId(userId, activityId);
           if(activityAccount == null) return 0;
           return activityAccount.getTotalCount() - activityAccount.getTotalCountSurplus();
       }finally {
           dbRouter.clear();
       }
    }

    @Override
    public ActivityAccountEntity queryActivityAccount(String userId, Long activityId) {
        return null;
    }

    @Override
    public List<Long> queryActivitySkuAll() {
        String pattern = Constants.Redis.SKU + "*";

        Iterable<String> keys = redisService.getKeysByPattern(pattern);
        List<Long> skus = new ArrayList<>();
        if(keys == null) return null;
        for (String key : keys) {
            Long sku = Long.valueOf(key.split(Constants.COLON)[1]);
            skus.add(sku);
        }
        return skus;
    }

    @Override
    public void clearActivitySkuJobQueue(Long sku) {
        String cacheKey = Constants.Redis.ACTIVITY_DELAY_JOB_QUEUE + sku;
        RBlockingQueue<ActivitySkuStockSubtractionJobData> blockingQueue = redisService.getBlockingQueue(cacheKey);
        blockingQueue.clear();
    }

    @Override
    public void updateActivitySkuStockOver(Long sku) {
        activitySkuDao.updateActivitySkuStockOver(sku);
    }

    @Override
    public void saveCreditPaySkuOrder(ActivitySkuOrderAggregate activitySkuOrderAggregate) {
        String userId = activitySkuOrderAggregate.getUserId();
        Long activityId = activitySkuOrderAggregate.getActivityId();
        Long sku = activitySkuOrderAggregate.getSku();

        ActivitySkuOrderEntity activitySkuOrderEntity = activitySkuOrderAggregate.getActivitySkuOrderEntity();
        ActivityCountEntity activityCountEntity = activitySkuOrderAggregate.getActivityCountEntity();

        ActivitySkuOrder activitySkuOrder = new ActivitySkuOrder();
        activitySkuOrder.setUserId(userId);
        activitySkuOrder.setActivityId(activityId);
        activitySkuOrder.setSku(sku);
        activitySkuOrder.setStrategyId(activitySkuOrderEntity.getStrategyId());
        activitySkuOrder.setOrderId(activitySkuOrderEntity.getOrderId());
        activitySkuOrder.setActivityName(activitySkuOrderEntity.getActivityName());
        activitySkuOrder.setOrderTime(activitySkuOrderEntity.getOrderTime());
        activitySkuOrder.setTotalCount(activityCountEntity.getTotalCount());
        activitySkuOrder.setMonthCount(activitySkuOrderEntity.getMonthCount());
        activitySkuOrder.setDayCount(activitySkuOrderEntity.getDayCount());
        activitySkuOrder.setState(activitySkuOrderEntity.getState().getCode());
        activitySkuOrder.setOutBusinessId(activitySkuOrderEntity.getOutBusinessId());
        activitySkuOrder.setTradeType(activitySkuOrderEntity.getTradeType().getCode());
        try{
            dbRouter.doRouter(userId);
            transactionTemplate.execute(status -> {
                try{
                    activitySkuOrderDao.insert(activitySkuOrder);
                    return 0;
                }catch (DuplicateKeyException e){
                    status.setRollbackOnly();
                    throw new AppException(ResponseCode.DUPLICATE_KEY_EXCEPTION.getCode(), ResponseCode.DUPLICATE_KEY_EXCEPTION.getInfo());
                }
            });
        }finally {
            dbRouter.clear();
        }
    }

    @Override
    public void updateCreditPayOrder(DeliverOrderEntity deliverOrderEntity) {
        String lockKey = Constants.Redis.USER_ACTIVITY_ACCOUNT_LOCK + deliverOrderEntity.getUserId();
        RLock lock = redisService.getLock(lockKey);
        try{
            lock.lock(3, TimeUnit.SECONDS);

            String userId = deliverOrderEntity.getUserId();
            String outBusinessId = deliverOrderEntity.getOutBusinessNo();

            dbRouter.doRouter(userId);
            ActivitySkuOrder activitySkuOrderReq = new ActivitySkuOrder();
            activitySkuOrderReq.setUserId(userId);
            activitySkuOrderReq.setOrderId(outBusinessId);

            ActivitySkuOrder activitySkuOrder = activitySkuOrderDao.queryActivitySkuOrder(activitySkuOrderReq);
            if(null == activitySkuOrder){
                return;
            }

            ActivityAccount activityAccount = new ActivityAccount();
            activityAccount.setUserId(userId);
            activityAccount.setActivityId(activitySkuOrder.getActivityId());
            activityAccount.setTotalCount(activitySkuOrder.getTotalCount());
            activityAccount.setTotalCountSurplus(activitySkuOrder.getTotalCount());
            activityAccount.setMonthCount(activitySkuOrder.getMonthCount());
            activityAccount.setDayCount(activitySkuOrder.getDayCount());

            ActivityMonthAccount activityMonthAccount = new ActivityMonthAccount();
            activityMonthAccount.setUserId(userId);
            activityMonthAccount.setActivityId(activitySkuOrder.getActivityId());
            activityMonthAccount.setMonthCount(activitySkuOrder.getMonthCount());
            activityMonthAccount.setMonthCountSurplus(activitySkuOrder.getMonthCount());
            activityMonthAccount.setMonth(ActivityMonthAccountEntity.currentMonth());

            ActivityDayAccount activityDayAccount = new ActivityDayAccount();
            activityDayAccount.setUserId(userId);
            activityDayAccount.setActivityId(activitySkuOrder.getActivityId());
            activityDayAccount.setDayCount(activitySkuOrder.getDayCount());
            activityDayAccount.setDayCountSurplus(activitySkuOrder.getDayCount());
            activityDayAccount.setDay(ActivityDayAccountEntity.currentDay());


            transactionTemplate.execute(status -> {
                try{
                    int count = activitySkuOrderDao.updateStateCompleted(outBusinessId);
                    if(1 != count){
                        status.setRollbackOnly();
                        return 0;
                    }
                    if(activityAccountDao.getActivityAccountByUserIdAndActivityId(userId, activitySkuOrder.getActivityId()) != null){
                        activityAccountDao.updateActivityAccount(activityAccount);
                    }
                    else{
                        activityAccountDao.insert(activityAccount);
                    }
                    activityMonthAccountDao.updateActivityMonthAccount(activityMonthAccount);
                    activityDayAccountDao.updateActivityDayAccount(activityDayAccount);
                    return 0;
                }catch (DuplicateKeyException e){
                    status.setRollbackOnly();
                    throw new AppException(ResponseCode.DUPLICATE_KEY_EXCEPTION.getCode(), ResponseCode.DUPLICATE_KEY_EXCEPTION.getInfo());
                }
            });
        }finally {
            dbRouter.clear();
            lock.unlock();
        }
    }

    @Override
    public List<ActivitySkuEntity> queryActivitySkuByActivityId(Long activityId) {
        String cacheKey = Constants.Redis.SKU_ACTIVITY + activityId;
        List<ActivitySkuEntity> activitySkuEntities = redisService.getValue(cacheKey);
        if(null != activitySkuEntities) return activitySkuEntities;

        List<ActivitySku> activitySkus = activitySkuDao.getActivitySkuByActivityId(activityId);
        activitySkuEntities = new ArrayList<>();
        for (ActivitySku activitySku : activitySkus) {
            ActivitySkuEntity activitySkuEntity = new ActivitySkuEntity();
            activitySkuEntity.setSku(activitySku.getSku());
            activitySkuEntity.setPayAmount(activitySku.getPayAmount());
            activitySkuEntities.add(activitySkuEntity);
        }

        redisService.setValue(cacheKey, activitySkuEntities);
        return activitySkuEntities;
    }

    @Override
    public Integer queryStockSurplusCount(Long sku) {
        String cacheKey = Constants.Redis.ACTIVITY_SKU_COUNT + sku;
        return redisService.getValue(cacheKey);
    }

    @Override
    public ActivitySkuOrderEntity queryUnPaidActivitySkuOrder(Long sku, String userId, String tradeType) {
        if(!TradeTypeVO.credit_pay.getCode().equals(tradeType)) return null;

        try{
            dbRouter.doRouter(userId);
            ActivitySkuOrder activitySkuOrderReq = new ActivitySkuOrder();
            activitySkuOrderReq.setUserId(userId);
            activitySkuOrderReq.setSku(sku);
            activitySkuOrderReq.setTradeType(tradeType);

            ActivitySkuOrder activitySkuOrder = activitySkuOrderDao.queryUnPaidActivitySkuOrder(activitySkuOrderReq);
            if(activitySkuOrder == null) return null;

            ActivitySkuOrderEntity activitySkuOrderEntity = new ActivitySkuOrderEntity();
            activitySkuOrderEntity.setSku(activitySkuOrder.getSku());
            activitySkuOrderEntity.setUserId(activitySkuOrder.getUserId());
            activitySkuOrderEntity.setActivityId(activitySkuOrder.getActivityId());
            activitySkuOrderEntity.setStrategyId(activitySkuOrder.getStrategyId());
            activitySkuOrderEntity.setOrderTime(activitySkuOrder.getOrderTime());
            activitySkuOrderEntity.setActivityName(activitySkuOrder.getActivityName());
            activitySkuOrderEntity.setOrderId(activitySkuOrder.getOrderId());
            activitySkuOrderEntity.setTotalCount(activitySkuOrder.getTotalCount());
            activitySkuOrderEntity.setMonthCount(activitySkuOrder.getMonthCount());
            activitySkuOrderEntity.setDayCount(activitySkuOrder.getDayCount());
            activitySkuOrderEntity.setState(ActivitySkuOrderStateVO.valueOf(activitySkuOrder.getState()));
            activitySkuOrderEntity.setOutBusinessId(activitySkuOrder.getOutBusinessId());

            return activitySkuOrderEntity;
        }finally {
            dbRouter.clear();
        }
    }
}
